package com.indigon.parkStory.model
{
	import com.indigon.parkStory.data.levels.LevelData;
	import com.indigon.parkStory.data.objects.*;
	import com.indigon.parkStory.model.objects.*;

	public class ModelMain
	{
		private static const MAX_FRAME_SKIP : uint = 10;
		private var _skipTicks   		    : Number;
		private var _nextTick    		    : Number;
		public  var framerate               : uint;
		
		public var data     : LevelData;
		
		private var _hasStarted : Boolean;
		public  var victoryMet  : Boolean;
		public  var defeatMet   : Boolean;
		
		private var _waves            : Vector.<WaveModel>
		public var lillies            : Vector.<LillyModel>
		public var frog               : FrogModel;
		public var events             : Vector.<GameEvent>;
		
		
		// stats.
		public var score : ScoreModel;
		
		public function ModelMain(framerate:uint)
		{
			this.framerate = framerate;
			this._skipTicks = 1000 / framerate;
			this.score = new ScoreModel(this);
		}
		
		public function reset():void
		{
			_hasStarted = false;
			victoryMet  = false;
			defeatMet   = false;
			
			for each (var lilly : LillyModel in lillies)
			{
				lilly.uninit();
			}
			lillies = null;
			
			for each (var wave : WaveModel in _waves)
			{
				wave.uninit();
			}
			_waves = null;
			
			frog.uninit();
			frog = null;
			
			score.reset();
			
			events = null;
			data   = null;
		}
		
		public function config(level:LevelData):void
		{
			data = level;
			
			lillies = new Vector.<LillyModel>();
			
			// set lillies.
			for each (var lillyData : LillyData in level.lillies)
			{
				var newLilly : LillyModel = new LillyModel(lillyData, this);
				lillies.push(newLilly);
				newLilly.set(lillyData.pos.x, lillyData.pos.y);
				
				// set frog.
				if (lillyData.hasFrog)
				{
					frog  = new FrogModel(this);
					frog.setInitialLilly(newLilly);
				}
			}
			
			// set misc.
			_waves = new Vector.<WaveModel>();
		}
		
		public function start():void
		{
			clearEvents();
		}
		
		public function resume():void
		{
			_nextTick = new Date().getTime();
		}
		
		public function end():void
		{
			if (victoryMet)
			{
//				score.setFinalScore();
			}
		}
		
		public function addEvent(type:uint, object:ObjectModel=null):void
		{
			events.push(new GameEvent(type, object));
		}
		
		public function clearEvents():void
		{
			events = new Vector.<GameEvent>();
		}
		
		public function addWave(x:Number, y:Number, force:Number, generatedBy:LillyModel):void
		{
			var wave : WaveModel = new WaveModel(x, y, force, generatedBy, this);
			_waves.push(wave);
			addEvent(GameEvent.WAVE_GEN, wave);
		}
		
		public function resetCombo():void
		{
			frog.resetRange();
			score.resetCombo();
			addEvent(GameEvent.RESET_COMBO);
		}

		public function update():void
		{
			var loops : uint   = 0;
			var time  : Number = new Date().getTime();
			
			while ((time > _nextTick) && (loops < MAX_FRAME_SKIP))
			{
				// score and time.
				if (_hasStarted && !defeatMet && !victoryMet)
				{	
					score.update();
				}
				
				// frog.
				if (frog.willJump)
				{
					frog.jump();
					if (!_hasStarted)
					{
						_hasStarted = true;
					}
				}
				frog.update();
				
				// waves.
				var waveFunction : Function = function(wave:WaveModel, index:uint, vec:Vector.<WaveModel>):Boolean
				{
					wave.update();
					return !wave.isDead;
				}
				_waves = _waves.filter(waveFunction);
				
				// collisions.
				for (var i : uint = 0; i < lillies.length; i++)
				{
					for (var j : uint = i+1; j < lillies.length; j++)
					{
						lillies[i].checkCollision(lillies[j]);
					}
				}
				
				// lillies.
				var lillyFunction : Function = function(lilly:LillyModel, index:uint, vec:Vector.<LillyModel>):Boolean
				{
					lilly.update();
					return !lilly.isDead;
				}
				lillies = lillies.filter(lillyFunction);
				
				// end game.
				if ((score.collectedGoals == data.goals) && !victoryMet)
				{
					addEvent(GameEvent.WIN_LEVEL);
					victoryMet = true;
				}
				else if (frog.isDead && !defeatMet)
				{
					addEvent(GameEvent.LOSE_LEVEL);
					defeatMet = true;
				}
				
				// game loop mechanics.
				_nextTick += _skipTicks;
				loops++;
			}
		}
	}
}